<?php

namespace process;

use app\service\modbus\ModbusTcpService;
use plugin\admin\app\model\IotCollectDatum;
use plugin\admin\app\model\IotCollectDetail;
use plugin\admin\app\model\IotCollectTask;
use plugin\admin\app\model\IotDevice;
use plugin\admin\app\model\IotSignal;
use support\Log;
use Workerman\Connection\AsyncTcpConnection;
use Workerman\Crontab\Crontab;
use Workerman\Mqtt\Client;

class ModbusTask
{
    public function onWorkerStart()
    {
        $iot_collect_task = IotCollectTask::where(['status' => 'start'])->get();
        if ($iot_collect_task) {
            foreach ($iot_collect_task as $value) {
                $cron = $value->cron;
                $task_id = $value->id;
                // 每秒钟执行一次
                new Crontab($cron, function () use ($task_id) {
                    $this->getModbusTcpDeviceData($task_id);
                });
            }
        }

    }

    /**
     * 执行数据
     * @param int $task_id
     * @return void
     */
    protected function getModbusTcpDeviceData(int $task_id)
    {
        $date = date("Y-m-d H:i:s");
        $iot_collect_detail_data = IotCollectDetail::with(['get_device', 'get_child_device', 'get_point_group'])->where(['storage_action' => 'STORE:MYSQL'])->where("collect_task_id", $task_id)->get();
        if ($iot_collect_detail_data) {
            foreach ($iot_collect_detail_data as $value) {
                $collect_action = $value->collect_action;
                $lecp_server_name = ($value->get_device)['lecp_server_name'];
                $is_lecp_server = ($value->get_device)['is_lecp_server'];
                $device_name = ($value->get_device)['name'];
                $device_id = $value->device_id;
                $host = ($value->get_device)['ip'];
                $port = ($value->get_device)['port'];
                $signal_json = ($value->get_point_group)['signal_json'];
                if (strstr($collect_action, 'PLC:')) {
                    $uri = "http://{$host}:{$port}";
                    if ($signal_json) {
                        $signal_json_data = explode(',', $signal_json);
                        $signal_data = IotSignal::whereIn('id', $signal_json_data)->where('type', 1)->select(['id', 'name', 'address', 'type', 'field_name', 'field_type'])->get();
                        if ($signal_data) {
                            $action = "plc_read_node";
                            $date = date("Y-m-d H:i:s");
                            foreach ($signal_data as $_v) {
                                $address = $_v->address;
                                $field_type = $_v->field_type;
                                if ($is_lecp_server == 1 && $lecp_server_name) {
                                    $node = "NODES.{$lecp_server_name}.{$address}";
                                    $data = json_encode(['action' => $action, 'node' => $node]);
                                    $rtval = http_post($uri, $data);
                                    if ($rtval) {
                                        $add['device_id'] = $device_id;
                                        $add['child_device_id'] = 0;
                                        $add['address'] = $address;
                                        $add['field_type'] = $field_type;
                                        $add['signal_id'] = $_v->id;
                                        $add['collect_task_id'] = $task_id;
                                        $add['status'] = 1;
                                        $add['value'] = $value;
                                        $add['collect_time'] = $date;
                                        $add['create_time'] = $date;
                                        if ($rtval['errcode'] == 0) {
                                            //获取数据成功
                                            $value = $rtval['rtval'][0];
                                            $add['reason'] = "数据抓取正常:LECPServer正常";
                                        } else {
                                            $value = "";
                                            $add['reason'] = "数据抓取正常:获取LECPServer数据错误";
                                        }
                                        IotCollectDatum::insert($add);
                                    } else {
                                        //获取数据成功
                                        $add['device_id'] = $device_id;
                                        $add['child_device_id'] = 0;
                                        $add['address'] = $address;
                                        $add['field_type'] = -1;
                                        $add['signal_id'] = $_v->id;
                                        $add['collect_task_id'] = $task_id;
                                        $add['status'] = 0;
                                        $add['value'] = 0;
                                        $add['collect_time'] = $date;
                                        $add['create_time'] = $date;
                                        $add['reason'] = "数据抓异常:LECPServer未启动";
                                        IotCollectDatum::insert($add);
                                    }
                                }
                            }
                        }
                    }
                } elseif (strstr($collect_action, "CLIENT:MODBUS:")) {
                    $uri = "tcp://{$host}:{$port}";
                    $collect_detail_id = $value->child_device_id;
                    $modbus_service_list = new ModbusTcpService();
                    //判断设备是否在线
                    $modbus_service = new ModbusTcpService($host, $port);
                    $isOnline = $modbus_service->isOnline();
                    if ($isOnline == "online") {
                        //在线进行数据获取 点位地址
                        if ($signal_json) {
                            $signal_json_data = explode(',', $signal_json);
                            $signal_data = IotSignal::whereIn('id', $signal_json_data)->where('type', 1)->select(['id', 'name', 'address', 'type', 'field_name', 'field_type','field_bit_address'])->get();
                            if ($signal_data) {
                                foreach ($signal_data as $_value) {
                                    $field_type = $_value->field_type;
                                    $field_type_name = "string";
                                    if ($field_type == 0) {
                                        $field_type_name = "bit";
                                    } elseif ($field_type == 1) {
                                        $field_type_name = "int16";
                                    } elseif ($field_type == 2) {
                                        $field_type_name = "uint16";
                                    } elseif ($field_type == 3) {
                                        $field_type_name = "int32";
                                    } elseif ($field_type == 4) {
                                        $field_type_name = "uint32";
                                    } elseif ($field_type == 5) {
                                        $field_type_name = "uint64";
                                    } elseif ($field_type == 6) {
                                        $field_type_name = "float";
                                    } elseif ($field_type == 8) {
                                        $field_type_name = "double";
                                    } elseif ($field_type == 9) {
                                        $field_type_name = "bit";
                                    } elseif ($field_type == 10) {
                                        $field_type_name = "byte";
                                    } elseif ($field_type == 11) {
                                        $field_type_name = "int64";
                                    }
                                    if ($field_type_name=="bit"){
                                        $bit = $value->field_bit_address;
                                        $arr = [
                                            ['uri' => $uri, 'start_address' => $_value->address, 'get_type' => $field_type_name,'bit'=>$bit, 'name' => $_value->field_name]
                                        ];
                                    }elseif ($field_type_name=="byte"){
                                        $field_bit_address = $value->field_bit_address;
                                        $byte=false;
                                        if ($field_bit_address){
                                            $byte=true;
                                        }
                                        $arr = [
                                            ['uri' => $uri, 'start_address' => $_value->address, 'get_type' => $field_type_name,'byte'=>$byte, 'name' => $_value->field_name]
                                        ];
                                    }else{
                                        $arr = [
                                            ['uri' => $uri, 'start_address' => $_value->address, 'get_type' => $field_type_name, 'name' => $_value->field_name]
                                        ];
                                    }
                                    $field_name = $_value->field_name;
                                    //获取到数据
                                    $date = date("Y-m-d H:i:s");
                                    $get_data = $modbus_service_list->getModbusTcpTypeMultipleRead($arr);
                                    $add['device_id'] = $device_id;
                                    $add['child_device_id'] = $collect_detail_id;
                                    $add['collect_task_id'] = $task_id;
                                    $add['collect_time'] = $date;
                                    $add['create_time'] = $date;
                                    if (is_array($get_data)) {
                                        $add['address'] = $_value->address;
                                        $add['field_type'] = $field_type;
                                        $add['signal_id'] = $_value->id;
                                        $add['status'] = 1;
                                        $add['value'] = $get_data[$field_name];
                                        $add['reason'] = "数据抓取正常";
                                    } else {
                                        $add['address'] = 0;
                                        $add['field_type'] = -1;
                                        $add['signal_id'] = 0;
                                        $add['status'] = 0;
                                        $add['value'] = "";
                                        $add['reason'] = "数据抓取异常: " . $get_data;
                                    }
                                    IotCollectDatum::insert($add);
                                }
                            }
                        }
                    } else {
                        $add['device_id'] = $device_id;
                        $add['child_device_id'] = $collect_detail_id;
                        $add['address'] = 0;
                        $add['field_type'] = -1;
                        $add['signal_id'] = 0;
                        $add['collect_task_id'] = $task_id;
                        $add['status'] = 0;
                        $add['value'] = 0;
                        $add['collect_time'] = $date;
                        $add['create_time'] = $date;
                        $add['reason'] = "设备【" . $device_name . "】未启动,需要核实信息";
                        IotCollectDatum::insert($add);
                    }
                    //设备不在线记录日志
                } elseif ($collect_action == "CLIENT:MQTT") {
                    if ($signal_json) {
                        $signal_json_data = explode(',', $signal_json);
                        $signal_data = IotSignal::whereIn('id', $signal_json_data)->where('type', 3)->select(['id', 'name', 'address', 'type', 'field_name', 'field_type'])->get();
                        //获取需要订阅的主题
                        $subscribe = [];
                        foreach ($signal_data as $k => $v) {
                            $id = $device_id;
                            $address = $v->address;
                            $address = str_replace('{$id}', $id, $address);
                            $subscribe[$address] = $k;
                        }
                        try {
                            $options = [];
                            $mqtt = new Client("mqtt://{$host}:{$port}", $options);
                            if ($subscribe) {
                                $mqtt->onConnect = function ($mqtt) use ($subscribe, $device_id) {
                                    //订阅主题
                                    $mqtt->subscribe($subscribe);
                                };
                                $mqtt->onMessage = function ($topic, $content) use ($mqtt, $device_id, $signal_data, $task_id) {
                                    $status = IotCollectTask::where(['id' => $task_id])->value("status");
                                    if ($status == "stop") {
                                        $mqtt->close();
                                    } else {
                                        $topic = str_replace($device_id, '{$id}', $topic);
                                        $signal_id = IotSignal::where('address', $topic)->value('id');
                                        //获取需要订阅的主题对应的内容
                                        $date = date("Y-m-d H:i:s");
                                        $exists = IotCollectDatum::where(['address' => $topic, 'value' => $content, 'collect_time' => $date])->exists();
                                        if (empty($exists)) {
                                            $add['device_id'] = $device_id;
                                            $add['child_device_id'] = 0;
                                            $add['address'] = $topic;
                                            $add['field_type'] = 7;
                                            $add['signal_id'] = $signal_id;
                                            $add['collect_task_id'] = $task_id;
                                            $add['status'] = 1;
                                            $add['value'] = $content;
                                            $add['collect_time'] = $date;
                                            $add['create_time'] = $date;
                                            $add['reason'] = "数据抓取正常";
                                            IotCollectDatum::insert($add);
                                        }
                                    }
                                };
                                $mqtt->onClose = function ($mqtt) use ($task_id) {
                                    IotCollectTask::where('id', $task_id)->update(['status' => "stop"]);
                                    $mqtt->close();
                                };
                                $mqtt->connect();
                            }
                        } catch (\Exception $e) {
                            //出现异常的情况下
                            Log::info("modbusTask的错误信息", ['error' => $e->getMessage()]);
                            $add['device_id'] = $device_id;
                            $add['child_device_id'] = 0;
                            $add['address'] = "";
                            $add['field_type'] = 7;
                            $add['signal_id'] = "";
                            $add['collect_task_id'] = $task_id;
                            $add['status'] = 0;
                            $add['value'] = "";
                            $add['collect_time'] = $date;
                            $add['create_time'] = $date;
                            $add['reason'] = "数据抓取异常,异常信息:" . $e->getMessage();
                            IotCollectDatum::insert($add);
                        }
                    }
                } elseif ($collect_action == "CLIENT:TCP") {
                    $connection = new AsyncTcpConnection("tcp://{$host}:{$port}");
                    $connection->onConnect=function (AsyncTcpConnection $connection)use($signal_json){
                        if ($signal_json) {
                            $signal_json_data = explode(',', $signal_json);
                            $signal_data = IotSignal::whereIn('id', $signal_json_data)->where('type', 2)->select(['id', 'name', 'address', 'type', 'field_name', 'field_type', 'message', 'encode'])->get();
                            if ($signal_data) {
                                foreach ($signal_data as $_v) {
                                    $encode = $_v->encode;
                                    $message = $_v->message;
                                    if ($encode==1 && $message){
                                        //这里进行数据处理
                                        //$connection->send($message);
                                    }
                                }
                            }
                        }
                    };
                    $connection->onMessage=function (AsyncTcpConnection $connection, $buffer)use($signal_json, $task_id){
                        $status = IotCollectTask::where(['id' => $task_id])->value("status");
                        if ($status == "stop") {
                            //$connection->close();
                        }else{
                            //todo 【未完】根据类型回去返回来的数据
                            var_dump(bin2hex($buffer));
                        }
                    };
                    $connection->onClose=function (AsyncTcpConnection $connection)use($task_id,$date){
                        $connection->close();
                    };
                    $connection->connect();
                }
            }
        }
    }

}
